import { switchMap, map } from 'rxjs/operators'
import { Observable, Subscriber, from } from 'rxjs'
import { Injectable } from '@nestjs/common'
import {
  chmod,
  PathLike,
  chown,
  close,
  copyFile,
  exists,
  mkdir,
  open,
  read,
  readdir,
  rename,
  readFile,
  writeFile
} from 'fs'
import { dirname } from 'path'
@Injectable()
export class FsService {
  // 写入文件
  file_put_contents(path: string, data: any) {
    const pathDirname = dirname(path)
    return this.exists(pathDirname).pipe(
      switchMap((exist: boolean) => {
        if (exist) {
          this.writeFile(path, data)
        } else {
          return this.mkdir(pathDirname, '0777')
        }
      })
    )
  }

  writeFile(path: string, data: any) {
    return from(data).pipe(
      map(res => {
        if (typeof res === 'string') {
          return res
        } else {
          return JSON.stringify(res)
        }
      }),
      switchMap(res => {
        return Observable.create((obser: Subscriber<any>) => {
          writeFile(path, data, {}, err => {
            if (err) {
              obser.error(err)
            }
            obser.next()
            obser.complete()
          })
        })
      })
    )
  }

  /**
   * 异步地改变文件的权限
   * @param path
   * @param mode
   */
  chmod(path: PathLike, mode: string | number): Observable<void> {
    return Observable.create((obser: Subscriber<void>) => {
      chmod(path, mode, (err: NodeJS.ErrnoException) => {
        if (err) {
          obser.error(err)
        }
        obser.next()
        obser.complete()
      })
    })
  }

  chown(path: PathLike, uid: number, gid: number): Promise<void> {
    return Observable.create((obser: Subscriber<void>) => {
      chown(path, uid, gid, (err: NodeJS.ErrnoException) => {
        if (err) {
          obser.error(err)
        }
        obser.next()
        obser.complete()
      })
    })
  }

  close(fd: number): Observable<void> {
    return Observable.create((obser: Subscriber<void>) => {
      close(fd, (err: NodeJS.ErrnoException) => {
        if (err) {
          obser.error(err)
        }
        obser.next()
        obser.complete()
      })
    })
  }

  copyFile(src: PathLike, dest: PathLike): Observable<void> {
    return Observable.create((obser: Subscriber<void>) => {
      copyFile(src, dest, (err: NodeJS.ErrnoException) => {
        if (err) {
          obser.error(err)
        }
        obser.next()
        obser.complete()
      })
    })
  }

  exists(path: PathLike): Observable<boolean> {
    return Observable.create((obser: Subscriber<boolean>) => {
      exists(path, (exists: boolean) => {
        obser.next(exists)
        obser.complete()
      })
    })
  }

  mkdir(path: PathLike, mode: number | string | undefined | null): Observable<void> {
    return Observable.create((obser: Subscriber<void>) => {
      mkdir(path, mode, (err: NodeJS.ErrnoException) => {
        if (err) {
          obser.error(err)
        }
        obser.next()
        obser.complete()
      })
    })
  }

  open(
    path: PathLike,
    flags: string | number,
    mode: string | number | undefined | null
  ): Observable<number> {
    return Observable.create((obser: Subscriber<number>) => {
      open(path, flags, (err: NodeJS.ErrnoException, fd: number) => {
        if (err) {
          obser.error(err)
        }
        obser.next(fd)
        obser.complete()
      })
    })
  }

  read<T extends Buffer | Uint8Array>(
    fd: number,
    buffer: T,
    offset: number,
    length: number,
    position: number | null
  ): Observable<{ bytesRead: number; buffer: T }> {
    return Observable.create((obser: Subscriber<{ bytesRead: number; buffer: T }>) => {
      read(
        fd,
        buffer,
        offset,
        length,
        position,
        (err: NodeJS.ErrnoException, bytesRead: number, buffer: T) => {
          if (err) {
            obser.error(err)
          }
          obser.next({
            bytesRead: bytesRead,
            buffer: buffer
          })
          obser.complete()
        }
      )
    })
  }

  readdir(
    path: PathLike,
    options: { encoding: BufferEncoding | null } | BufferEncoding | undefined | null
  ): Observable<string[]> {
    return Observable.create((obser: Subscriber<string[]>) => {
      readdir(path, options, (err: NodeJS.ErrnoException, files: string[]) => {
        if (err) {
          obser.error(err)
        }
        obser.next(files)
        obser.complete()
      })
    })
  }

  readFile(
    path: PathLike | number,
    options: { encoding?: null; flag?: string } | undefined | null
  ): Observable<Buffer> {
    return Observable.create((obser: Subscriber<Buffer>) => {
      readFile(path, options, (err: NodeJS.ErrnoException, data: Buffer) => {
        if (err) {
          obser.error(err)
        }
        obser.next(data)
        obser.complete()
      })
    })
  }

  rename(oldPath: PathLike, newPath: PathLike): Observable<void> {
    return Observable.create((obser: Subscriber<void>) => {
      rename(oldPath, newPath, (err: NodeJS.ErrnoException) => {
        if (err) {
          obser.error(err)
        }
        obser.next()
        obser.complete()
      })
    })
  }

  fstat() {}

  access() {}

  appendFile() {}

  rmdir() {}

  stat() {}

  unlink() {}

  watch() {}
}
